namespace SimpleWebServices.Web.Mvc
{
    using System;
    using System.Net;
    using System.Web;
    using System.Web.Mvc;
    using SimpleWebServices.IO;

    [AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)]
    public sealed class AllowAttribute : ActionFilterAttribute
    {
        public AllowAttribute()
        {
            this.Methods = "GET, HEAD, OPTIONS";
        }

        public string Methods
        {
            get;
            set;
        }

        public override void OnActionExecuting(ActionExecutingContext filterContext)
        {
            if (null == filterContext)
            {
                throw new ArgumentNullException("filterContext");
            }

            base.OnActionExecuting(filterContext);

            HttpRequestBase request = filterContext.HttpContext.Request;
            HttpResponseBase response = filterContext.HttpContext.Response;

            bool options = "OPTIONS".Equals(request.HttpMethod, StringComparison.OrdinalIgnoreCase);

            if (options || this.IsNotAllowed(request.HttpMethod))
            {
                response.Clear();
                WrappedStream filter = response.Filter as WrappedStream;
                if (null != filter)
                {
                    response.Filter = filter.UnderlyingStream;
                }

                if (options)
                {
                    response.StatusCode = (int)HttpStatusCode.OK;
                }
                else
                {
                    response.StatusCode = (int)HttpStatusCode.MethodNotAllowed;
                }

                response.AppendHeader("Allow", this.Methods ?? "OPTIONS");
                response.Cache.SetCacheability(HttpCacheability.Public);
                response.Cache.SetExpires(DateTimeFactory.UtcNow.AddDays(1));
                response.End();
            }
        }

        private bool IsNotAllowed(string method)
        {
            bool allowed = false;

            if (!string.IsNullOrEmpty(this.Methods))
            {
                string[] parts = this.Methods.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
                foreach (string part in parts)
                {
                    if (part.Trim().Equals(method, StringComparison.OrdinalIgnoreCase))
                    {
                        allowed = true;
                    }
                }
            }

            return !allowed;
        }
    }
}